// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 一般环境变量。

package os

import (
	"internal/testlog"
	"syscall"
)

// Expand根据映射函数替换字符串中的${var}或$var。
// 例如，os.ExpandEnv相当于os.Expand（s，os.Getenv）。
func Expand(s string, mapping func(string) string) string {
	var buf []byte
	// /${}都是ASCII码，所以字节可以用于此操作。
	i := 0
	for j := 0; j < len(s); j++ {
		if s[j] == '$' && j+1 < len(s) {
			if buf == nil {
				buf = make([]byte, 0, 2*len(s))
			}
			buf = append(buf, s[i:j]...)
			name, w := getShellName(s[j+1:])
			if name == "" && w > 0 {
				// 遇到无效语法；吃
				// 字符。
			} else if name == "" {
				// 语法有效，但$后面没有
				// 名称。保持美元字符不变。
				buf = append(buf, s[j])
			} else {
				buf = append(buf, mapping(name)...)
			}
			j += w
			i = j + 1
		}
	}
	if buf == nil {
		return s
	}
	return string(buf) + s[i:]
}

// ExpandEnv根据当前环境变量的值
// 替换字符串中的${var}或$var。对未定义
// 变量的引用将替换为空字符串。
func ExpandEnv(s string) string {
	return Expand(s, Getenv)
}

// isShellSpecialVar报告字符是否标识特殊的
// shell变量，如$*。
func isShellSpecialVar(c uint8) bool {
	switch c {
	case '*', '#', '$', '@', '!', '?', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
		return true
	}
	return false
}

// isAlphaNum报告字节是ASCII字母、数字还是下划线
func isAlphaNum(c uint8) bool {
	return c == '_' || '0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z'
}

// getShellName返回字符串开头的名称以及提取字符串所使用的字节数
// 。如果名称包含在{}中，则它是${}
// 扩展的一部分，并且需要比名称长度多两个字节。
func getShellName(s string) (string, int) {
	switch {
	case s[0] == '{':
		if len(s) > 2 && isShellSpecialVar(s[1]) && s[2] == '}' {
			return s[1:2], 3
		}
		// 扫描到右大括号
		for i := 1; i < len(s); i++ {
			if s[i] == '}' {
				if i == 1 {
					return "", 2 // 语法错误；eat“${}”
				}
				return s[1:i], i + 1
			}
		}
		return "", 1 // 语法错误；吃“${”
	case isShellSpecialVar(s[0]):
		return s[0:1], 1
	}
	// 扫描字母数字。
	var i int
	for i = 0; i < len(s) && isAlphaNum(s[i]); i++ {
	}
	return s[:i], i
}

// Getenv检索由键命名的环境变量的值。
// 返回值，如果变量不存在，该值将为空。
// 要区分空值和未设置的值，请使用LookupEnv.
func Getenv(key string) string {
	testlog.Getenv(key)
	v, _ := syscall.Getenv(key)
	return v
}

// LookupEnv通过键检索名为
// 的环境变量的值。如果环境中存在该变量，则返回
// 值（可能为空）返回，布尔值为true。否则返回值将为空，布尔值将为false。
func LookupEnv(key string) (string, bool) {
	testlog.Getenv(key)
	return syscall.Getenv(key)
}

// Setenv设置由键命名的环境变量的值。
// 如果有错误，则返回错误。
func Setenv(key, value string) error {
	err := syscall.Setenv(key, value)
	if err != nil {
		return NewSyscallError("setenv", err)
	}
	return nil
}

// Unsetenv取消设置单个环境变量ABABLE.
func Unsetenv(key string) error {
	return syscall.Unsetenv(key)
}

// Clearenv删除所有环境变量。
func Clearenv() {
	syscall.Clearenv()
}

// Environ返回表示环境的字符串副本，
// ，格式为“key=value”。
func Environ() []string {
	return syscall.Environ()
}
